package helper

import (
	cRand "crypto/rand"
	"errors"
	"fmt"
	"image/color"
	"math"
	"math/big"
	"math/rand"
	"net/http"
	"os"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/google/uuid"
)

var (
	noWordReg = regexp.MustCompile(`\W`)
	regMobile = regexp.MustCompile(`^1[3456789]{1}\d{9}$`)
	dateMap   = map[string]string{
		"Y": "2006",
		"m": "01",
		"d": "02",
		"H": "15",
		"i": "04",
		"s": "05",
	}
)

// CheckZhMobileFormat 验证中国手机号码格式
func CheckZhMobileFormat(mobile string) bool {
	return regMobile.MatchString(mobile)
}

// FormatInterPhone 格式电话(国际版)
func FormatInterPhone(tel, prev, showPrev string) string {
	tel = noWordReg.ReplaceAllString(tel, "")
	regStr := fmt.Sprintf(`^\+?(%s)?0?`, regexp.QuoteMeta(prev))
	if reg, err := regexp.Compile(regStr); err == nil {
		tel = reg.ReplaceAllString(tel, showPrev)
	}
	return tel
}

// HideMobileMiddleChar 隐藏中间部分字符
func HideMobileMiddleChar(mobile string) string {
	length := len(mobile)
	if length > 0 && length <= 6 {
		mobile = "***"
	} else if length > 6 && length <= 10 {
		mobile = mobile[:2] + "***" + mobile[length-2:]
	} else if length > 10 {
		mobile = mobile[:3] + "***" + mobile[length-4:]
	}
	return mobile
}

// HideMiddleString 隐藏中间部分字符串
func HideMiddleString(str string, maxPre, maxSuf, minHideCount uint, xCount int, xChar string) string {
	arr := []rune(str)
	length := uint(len(arr))
	ret := ""
	if length > 0 {
		if xCount > 0 {
			for i := 0; i < xCount; i++ {
				ret += xChar
			}
		}
		if minHideCount <= length {
			length -= minHideCount
			if maxSuf == 0 {
				maxSuf = length / 2
			}
			if maxPre == 0 {
				maxPre = length - maxSuf
			}
			if length < maxPre {
				maxPre = length
				maxSuf = 0
			} else {
				_length := length - maxPre
				if _length < maxSuf {
					maxSuf = _length
				}
			}
			if minHideCount == 0 && maxPre+maxSuf < length {
				minHideCount = length - maxPre - maxSuf
			}
			ret = string(arr[:maxPre]) + ret
		} else {
			minHideCount = length
			maxSuf = 0
		}
		if xCount <= 0 {
			for i := 0; i < int(minHideCount); i++ {
				ret += xChar
			}
		}
		ret = ret + string(arr[uint(len(arr))-maxSuf:])
	}
	return ret
}

// AutoHideString 自动化隐藏字条
func AutoHideString(str string) string {
	length := len([]rune(str))
	if length >= 15 {
		return HideMiddleString(str, 6, 4, 0, 3, "*")
	} else if length > 8 {
		return HideMiddleString(str, 3, 4, 0, 3, "*")
	} else if length > 5 {
		return HideMiddleString(str, 2, 2, 0, 3, "*")
	} else if length > 2 {
		return HideMiddleString(str, 1, 1, 0, 3, "*")
	} else {
		return HideMiddleString(str, 1, 0, 0, 3, "*")
	}
}

// FloatTwoDecimal 最多保留2位非0小数点
func FloatTwoDecimal(val float64) (float64, error) {
	return strconv.ParseFloat(fmt.Sprintf("%.2f", val), 64)
}

// PathExists 路径(文件或目录)检测是否存在
func PathExists(path string) bool {
	_, err := os.Stat(path)
	if err != nil {
		return os.IsExist(err)
	}
	return true
}

// CheckAndMkDir 检测目录(不存在时新建)
func CheckAndMkDir(path string, pem os.FileMode) (err error) {
	pathArr := strings.Split(strings.TrimSpace(path), "/")
	for k, v := range pathArr {
		if v != "" {
			_path := strings.Join(pathArr[:k+1], "/")
			if !PathExists(_path) {
				if err = os.Mkdir(_path, pem); err != nil {
					return
				}
			}
		}
	}
	return
}

// RandNumberCode 随机生成指定位置的验证码(注: n最大值为9)
func RandNumberCode(n int) string {
	rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
	_n := math.Pow10(n)
	if _n > float64(math.MaxInt32) {
		return RandString(n, 2)
	}
	return fmt.Sprintf("%"+fmt.Sprintf("0%dv", n), rnd.Int31n(int32(_n)))
}

// RandString 随机生成字条串
// @param  t int  0 字母与数字, 1 仅字母, 2 仅数字
func RandString(length int, t int) string {
	strs := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
	if t == 1 {
		strs = strs[:len(strs)-10]
	} else if t == 2 {
		strs = strs[len(strs)-10:]
	}
	ret := ""
	bigInt := big.NewInt(int64(len(strs)))
	for i := 0; i < length; i++ {
		_int, _ := cRand.Int(cRand.Reader, bigInt)
		index := _int.Int64()
		ret += strs[index : index+1]
	}
	return ret
}

// Trim 去掉字符串左右两边的空白符(空格、制表、回车、换行、垂直制表符)
func Trim(str string) string {
	return strings.Trim(str, " \t\n\r\x0B")
}

// GenerateUUID 生成uuid
func GenerateUUID() string {
	u4 := uuid.New()
	return strings.ReplaceAll(u4.String(), "-", "")
}

// Round 四舍五入
func Round(x float64) int {
	return int(math.Floor(x + 0.5))
}

// GetDayStartByTime 获取指定时间那天的起始时间戳
// 8小时的时差
func GetDayStartByTime(t time.Time) int64 {
	return t.Unix()/(24*60*60)*24*60*60 - (8 * 60 * 60)
}

// GetRequestIP 获取得请求ip
func GetRequestIP(r *http.Request) (ip string) {
	ip = r.Header.Get("X-Forwarded-For")
	ip = Trim(strings.Split(ip, ",")[0])
	if ip == "" {
		ip = Trim(r.Header.Get("X-Real-Ip"))
	}
	return
}

// ShuffleIntArr 随机排序int数组
func ShuffleIntArr(slice []int) {
	r := rand.New(rand.NewSource(time.Now().Unix()))
	for i := len(slice) - 1; i > 0; i-- {
		num := r.Intn(i + 1)
		slice[i], slice[num] = slice[num], slice[i]
	}
}

// ArrayUnique 去重
func ArrayUnique(arr []int) []int {
	m := map[int]int{}
	for _, v := range arr {
		m[v]++
	}
	ret := []int{}
	for _, v := range arr {
		if _, ok := m[v]; ok {
			ret = append(ret, v)
			delete(m, v)
		}
	}
	return ret
}

func InArray(arr []int, item int) bool {
	for _, v := range arr {
		if v == item {
			return true
		}
	}
	return false
}

// ExistItem 存在
func ExistItem(arr []string, item string) bool {
	for _, v := range arr {
		if v == item {
			return true
		}
	}
	return false
}

func SimpleKey(key string) bool {
	return !strings.Contains(key, ")") && !strings.Contains(key, " ")
}

// GetStructTag 获取struct的tag名称
func GetStructTag(tag string) string {
	if tag == "-" {
		tag = ""
	} else if tag != "" && SimpleKey(tag) {
		if i := strings.Index(tag, ","); i >= 0 {
			for _, v := range strings.Split(tag, ",") {
				if v != "" && v != "-" && v != "omitempty" {
					tag = v
					break
				}
			}
		}
	}
	return tag
}

// FormatToInterfaceMap 格式化成map[string]interface{}
// @Params excludes 可以为 field 或 tag
func FormatToInterfaceMap(x interface{}, tagName string, excludes []string) map[string]interface{} {
	ret := map[string]interface{}{}
	rType := reflect.TypeOf(x)
	if rType.Kind() == reflect.Ptr {
		rType = rType.Elem()
		if rType.Kind() == reflect.Struct {
			vType := reflect.ValueOf(x).Elem()
			exStr := fmt.Sprintf(",%s,", strings.Join(excludes, ","))
			for i := 0; i < rType.NumField(); i++ {
				tag := rType.Field(i).Tag.Get(tagName)
				chkName := "," + rType.Field(i).Name + ","
				if v := GetStructTag(tag); v != "" && !strings.Contains(exStr, chkName) &&
					!strings.Contains(exStr, fmt.Sprintf(",%s,", v)) {
					ret[v] = vType.Field(i).Interface()
				}
			}
		}
	}
	return ret
}

// IsMobile 是否为移动端
func IsMobile(userAgent string) bool {
	mobileKeywords := []string{
		"Mobile",
		"Android",
		"Silk/",
		"Kindle",
		"BlackBerry",
		"Opera Mini",
		"Opera Mobi",
	}
	for _, v := range mobileKeywords {
		if strings.Contains(userAgent, v) {
			return true
		}
	}
	return false
}

// IsWeiXin 是否处于微信端
func IsWeiXin(useragent string) bool {
	return strings.Contains(useragent, "MicroMessenger")
}

// CheckPrefixString 检查字符串前缀的/
func CheckPrefixString(str string, exsit bool) string {
	if len(str) > 0 {
		if !exsit && str[0] == '/' {
			str = str[1:]
		} else if exsit && str[0] != '/' {
			str = "/" + str
		}
	} else if exsit {
		str = "/"
	}
	return str
}

func HexToDecimal(hex string) (int64, error) {
	if strings.HasPrefix(hex, "0x") || strings.HasPrefix(hex, "0X") {
		hex = hex[2:]
	}
	return strconv.ParseInt(hex, 16, 0)
}

// web: opacity in subfix, opacityInPrefix = false
// android: opacity in prefix, opacityInPrefix = true
func HexToRgba(hex string, opacityInPrefix bool) (color.RGBA, error) {
	var (
		err            error
		r2, g2, b2, a2 uint64
		rgba           color.RGBA
	)

	rgba.A = 255
	hex = strings.TrimLeft(hex, "#")
	if len(hex) == 3 {
		hex = hex[:1] + hex[:1] + hex[1:2] + hex[1:2] + hex[2:3] + hex[2:3]
	}
	if len(hex) == 6 || len(hex) == 8 {
		if len(hex) == 8 {
			opacityStr := ""
			if opacityInPrefix {
				opacityStr = hex[:2]
				hex = hex[2:]
			} else {
				opacityStr = hex[6:]
				hex = hex[:6]
			}
			if a2, err = strconv.ParseUint(opacityStr, 16, 10); err != nil || a2 > math.MaxUint8 {
				err = errors.New("opacity value invalid")
				return rgba, err
			} else {
				rgba.A = uint8(a2)
			}
		}
		if r2, err = strconv.ParseUint(hex[:2], 16, 10); err == nil {
			if g2, err = strconv.ParseUint(hex[2:4], 16, 10); err == nil {
				if b2, err = strconv.ParseUint(hex[4:], 16, 10); err == nil {
					if b2 > math.MaxUint8 || g2 > math.MaxUint8 || r2 > math.MaxUint8 {
						err = errors.New("hex string value invalid")
					} else {
						rgba.R = uint8(r2)
						rgba.G = uint8(g2)
						rgba.B = uint8(b2)
					}
				}
			}
		}
	} else {
		err = errors.New("hex string length invalid")
	}
	return rgba, err
}

func SplitStringToInt(str string, sep string) (arr []int) {
	for _, v := range strings.Split(str, sep) {
		if v := strings.TrimSpace(v); v != "" {
			if v2, e := strconv.Atoi(v); e == nil {
				arr = append(arr, v2)
			}
		}
	}
	return
}

type IdCard struct {
	Province     string
	Gender       uint8 // 0 女 1 男
	Birthday     string
	BirthdayTime time.Time
}

func (id *IdCard) CheckMaxYear(year int) bool {
	return id.BirthdayTime.After(time.Now().Add(time.Hour * time.Duration(-24*365*year)))
}

func (id *IdCard) CheckMinYear(year int) bool {
	return id.BirthdayTime.Before(time.Now().Add(time.Hour * time.Duration(-24*365*year)))
}

/*
 CheckIdCard 判断18位身份证的合法性
 1. 前1、2位数字表示：所在省份的代码
 2. 第3、4位数字表示：所在城市的代码
 3. 第5、6位数字表示：所在区县的代码
 4. 第7~14位数字表示：出生年、月、日
 5. 第15、16位数字表示：所在地的派出所的代码
 6. 第17位数字表示性别：奇数表示男性，偶数表示女性
 7. 第18位为检验码, 验证规则如下:
	1). 从身份证的第1位至第17位分别乘于: 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2
	2). 将上一步17位相乘的结果相加
	3). 将相加的结果模于11
	4). 余数取出对应的数字(1 0 X 9 8 7 6 5 4 3 2)进行比较
*/
func CheckIdCard(str string) (IdCard, error) {
	var (
		err error
		ret IdCard
	)

	areaCode := map[string]string{
		"11": "北京",
		"12": "天津",
		"13": "河北",
		"14": "山西",
		"15": "内蒙古",
		"21": "辽宁",
		"22": "吉林",
		"23": "黑龙江",
		"31": "上海",
		"32": "江苏",
		"33": "浙江",
		"34": "安徽",
		"35": "福建",
		"36": "江西",
		"37": "山东",
		"41": "河南",
		"42": "湖北",
		"43": "湖南",
		"44": "广东",
		"45": "广西",
		"46": "海南",
		"50": "重庆",
		"51": "四川",
		"52": "贵州",
		"53": "云南",
		"54": "西藏",
		"61": "陕西",
		"62": "甘肃",
		"63": "青海",
		"64": "宁夏",
		"65": "新疆",
		"71": "台湾",
		"81": "香港",
		"82": "澳门",
		"91": "国外",
	}

	if regexp.MustCompile(`^\d{17}[\dxX]$`).MatchString(str) {
		if v, ok := areaCode[str[:2]]; ok {
			ret.Province = v
			// minDate, _ := time.ParseInLocation("2006", "1900", time.Local)
			// maxDate := time.Now().Add(-(time.Hour * time.Duration(24*365*minYear)))
			ret.Birthday = str[6:14]
			if t, e := time.ParseInLocation("20060102", ret.Birthday, time.Local); e == nil && t.Before(time.Now()) {
				ret.BirthdayTime = t
				// if t.Before(minDate) {
				// 	err = errors.New("出生日期超出最大限制")
				// } else if t.After(maxDate) {
				// 	err = errors.New("出生日期低于最小限制")
				// } else {
				gender, _ := strconv.Atoi(str[16:17])
				if gender%2 == 1 {
					ret.Gender = 1
				}
				wi := []int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}
				lastMap := []string{"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"}
				var total int
				for k2, v2 := range str[:17] {
					num, _ := strconv.Atoi(string(v2))
					total += num * wi[k2]
				}
				if v3 := lastMap[total%11]; v3 != strings.ToUpper(str[17:18]) {
					err = errors.New("检验码未通过: " + v3)
				}
				// }
			} else {
				err = errors.New("出生日期不合法")
			}
		} else {
			err = errors.New("地区不合法")
		}
	} else {
		err = errors.New("格式不合法")
	}
	return ret, err
}

func CheckProtectDir() (string, error) {
	var (
		err error
		ret string
	)

	if ret, err = os.Getwd(); err == nil {
		ret += "/"
		var protectDir string
		for i := len(ret); i > 0; i-- {
			if ret[i-1:i] == "/" {
				if PathExists(ret[:i] + "go.mod") {
					protectDir = ret[:i]
					break
				}
			}
		}
		ret = protectDir
	}
	return ret, err
}

func CompareVersion(version, targetVersion, symbol string) bool {
	symbolMap := map[string]func(int, int) bool{
		">":  func(t, v int) bool { return v > t },
		">=": func(t, v int) bool { return v >= t },
		"=":  func(t, v int) bool { return v == t },
		"<":  func(t, v int) bool { return v < t },
		"<=": func(t, v int) bool { return v <= t },
	}
	if fn, ok := symbolMap[symbol]; ok {
		vreg := regexp.MustCompile(`\d+(\.\d+)*`)
		targetVersion = vreg.FindString(targetVersion)
		version = vreg.FindString(version)
		if version != "" && targetVersion != "" {
			targetArr := strings.Split(targetVersion, ".")
			targetLen := len(targetArr)
			versionArr := strings.Split(version, ".")
			for i := targetLen - len(versionArr); i > 0; i-- {
				versionArr = append(versionArr, "0")
			}
			for k, v := range targetArr {
				targetN, _ := strconv.Atoi(v)
				versionN, _ := strconv.Atoi(versionArr[k])
				if k+1 < targetLen && targetN == versionN {
					continue
				}
				return fn(targetN, versionN)
			}
		}
	}
	return true
}
